﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using UtilZ.Dotnet.Ex.Cache;
using UtilZ.Dotnet.Ex.Exceptions;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// EventWaitHandle自动管理类
    /// </summary>
    public class ObjectEventWaitHandleManager : IDisposableZ
    {
        private readonly ObjectCache _cache;

        /// <summary>
        /// 对象释放标识[true:已释放;fasle:未已释放]
        /// </summary>
        private bool _disposed = false;
        /// <summary>
        /// 获取当前对象是否已释放[true:已释放;false:未释放]
        /// </summary>
        public bool Disposed
        {
            get { return _disposed; }
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        public ObjectEventWaitHandleManager()
        {
            _cache = new ObjectCache();
        }

        /// <summary>
        /// key:EventKey(object);value:(EventWaitHandleInfo)
        /// </summary>
        private readonly ConcurrentDictionary<object, EventWaitHandleInfo> _eventWaitHandleDic = new ConcurrentDictionary<object, EventWaitHandleInfo>();

        private void ExpirationCacheEntryRemovedCallback(CacheEntryRemovedArguments arguments)
        {
            try
            {
                if (arguments.RemovedReason != CacheEntryRemovedReason.Removed)
                {
                    var id = arguments.CacheItem.Value;
                    EventWaitHandleInfo eventWaitHandleInfo;
                    if (_eventWaitHandleDic.TryRemove(id, out eventWaitHandleInfo))
                    {
                        try
                        {
                            if (eventWaitHandleInfo != null)
                            {
                                eventWaitHandleInfo.Dispose();
                            }
                        }
                        catch (ObjectDisposedException)
                        { }
                    }
                }
            }
            catch (Exception ex)
            {
                try
                {
                    string err = $"方法{typeof(ObjectEventWaitHandleManager).Name}.{nameof(ExpirationCacheEntryRemovedCallback)}发生异常;RemovedReason:{arguments.RemovedReason},Key:{arguments.CacheItem.Key},Value:{arguments.CacheItem.Value}";
                    ZLoger.Error(ex, err);
                }
                catch (Exception ex2)
                {
                    ZLoger.Error(ex, ex2.Message);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="expirationMilliseconds">缓存项有效时间,小于等于0永不过期,单位/毫秒</param>
        private void AddIdToCache(object id, int expirationMilliseconds)
        {
            if (expirationMilliseconds <= 0)
            {
                return;
            }

            _cache.Set(id, id, expirationMilliseconds, new CacheEntryRemovedCallback(ExpirationCacheEntryRemovedCallback));
        }


        private void AutoDisposeNotify(EventWaitHandleInfo eventWaitHandleInfo)
        {
            RemoveEventWaitHandle(eventWaitHandleInfo.Id);
        }

        /// <summary>
        /// 创建并添加EventWaitHandle
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <param name="expirationMilliseconds">缓存项有效时间,小于等于0永不过期,单位/毫秒</param>
        /// <param name="tag">Tag</param>
        /// <returns>创建的EventWaitHandle</returns>
        public EventWaitHandleInfo Create(object id, int expirationMilliseconds = -1, object tag = null)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            var eventWaitHandleInfo = new EventWaitHandleInfo(id, AutoDisposeNotify, tag);
            if (this._eventWaitHandleDic.TryAdd(id, eventWaitHandleInfo))
            {
                AddIdToCache(id, expirationMilliseconds);
                return eventWaitHandleInfo;
            }

            eventWaitHandleInfo.Dispose();
            throw new ArgumentException($"key{id}已存在");
        }

        /// <summary>
        /// 添加外部创建的EventWaitHandle
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <param name="eventWaitHandle">EventWaitHandle</param>
        /// <param name="expirationMilliseconds">缓存项有效时间,小于等于0永不过期,单位/毫秒</param>
        /// <param name="tag">Tag</param>
        /// <returns>创建的EventWaitHandle</returns>
        public EventWaitHandleInfo Create(object id, EventWaitHandle eventWaitHandle, int expirationMilliseconds = -1, object tag = null)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            var eventWaitHandleInfo = new EventWaitHandleInfo(id, eventWaitHandle, tag);
            if (this._eventWaitHandleDic.TryAdd(id, eventWaitHandleInfo))
            {
                AddIdToCache(id, expirationMilliseconds);
                return eventWaitHandleInfo;
            }
            else
            {
                throw new ArgumentException($"key{id}已存在");
            }
        }

        /// <summary>
        /// 添加或更新外部创建的EventWaitHandle,如果id存在则替换并返回旧的项,否则则添加返回null
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <param name="eventWaitHandle">EventWaitHandle</param>
        /// <param name="expirationMilliseconds">缓存项有效时间,小于等于0永不过期,单位/毫秒</param>
        /// <param name="tag">Tag</param>
        /// <returns>旧的EventWaitHandle</returns>
        public EventWaitHandle CreateOrUpdate(object id, EventWaitHandle eventWaitHandle, int expirationMilliseconds = -1, object tag = null)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            EventWaitHandleInfo oldEventWaitHandleInfo = null;
            var newValue = new EventWaitHandleInfo(id, eventWaitHandle, tag);
            this._eventWaitHandleDic.AddOrUpdate(id, newValue, (key, oldValue) =>
            {
                oldEventWaitHandleInfo = oldValue;
                return newValue;
            });
            AddIdToCache(id, expirationMilliseconds);

            if (oldEventWaitHandleInfo != null)
            {
                return oldEventWaitHandleInfo.EventWaitHandle;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取EventWaitHandle,获取成功返回获取EventWaitHandle;失败返回null
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <returns>EventWaitHandle</returns>
        public EventWaitHandle GetEventWaitHandle(object id)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            EventWaitHandleInfo eventWaitHandleInfo = GetEventWaitHandleInfo(id);
            if (eventWaitHandleInfo != null)
            {
                return eventWaitHandleInfo.EventWaitHandle;
            }

            return null;
        }

        /// <summary>
        /// 获取EventWaitHandleInfo
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <returns>EventWaitHandleInfo</returns>
        public EventWaitHandleInfo GetEventWaitHandleInfo(object id)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            EventWaitHandleInfo eventWaitHandleInfo;
            this._eventWaitHandleDic.TryGetValue(id, out eventWaitHandleInfo);
            return eventWaitHandleInfo;
        }

        /// <summary>
        /// 移除并返回EventWaitHandle
        /// </summary>
        /// <param name="id">EventWaitHandle的唯一标识</param>
        /// <returns>EventWaitHandle</returns>
        public EventWaitHandleInfo RemoveEventWaitHandle(object id)
        {
            this.CheckDispose();

            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            EventWaitHandleInfo eventWaitHandleInfo;
            if (this._eventWaitHandleDic.TryRemove(id, out eventWaitHandleInfo))
            {
                _cache.Remove(id);
            }

            return eventWaitHandleInfo;
        }


        private void CheckDispose()
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.ToString());
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            if (this._disposed)
            {
                return;
            }
            this._disposed = true;

            EventWaitHandleInfo[] waitHandleInfos = this._eventWaitHandleDic.Values.ToArray();
            foreach (EventWaitHandleInfo waitHandleInfo in waitHandleInfos)
            {
                waitHandleInfo.Dispose();
            }
            this._eventWaitHandleDic.Clear();
        }
    }
}
